package bma.common.langutil.ai.common;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import bma.common.langutil.ai.AIUtil;
import bma.common.langutil.ai.stack.AIStack;
import bma.common.langutil.ai.vm.AIContext;
import bma.common.langutil.ai.vm.AIContextThread;
import bma.common.langutil.ai.vm.AIVM;
import bma.common.langutil.concurrent.TimerManager;
import bma.common.langutil.core.ObjectUtil;

/**
 * AI信号灯机制的支持
 * 
 * @author guanzhong
 * 
 * @param <RESP_TYPE>
 */
public class AIEvent<RESP_TYPE> {

	private boolean m_AutoReset = false;

	private boolean m_BeSign = false;

	private List<AIRuntime<RESP_TYPE>> waiting = new LinkedList<AIRuntime<RESP_TYPE>>();

	private TimerManager timer;

	protected AIEvent() {
		this(false);
	}

	public AIEvent(boolean bAutoReset) {
		this(false, bAutoReset);
	}

	public AIEvent(boolean initSign, boolean bAutoReset) {
		m_AutoReset = bAutoReset;
		m_BeSign = initSign;
	}

	public TimerManager getTimer() {
		return timer;
	}

	public void setTimer(TimerManager timer) {
		this.timer = timer;
	}

	public static <TYPE> AIEvent<TYPE> createAutoResetEvent() {
		return new AIEvent<TYPE>(true);
	}

	public static <TYPE> AIEvent<TYPE> createManulResetEvent() {
		return new AIEvent<TYPE>(false);
	}

	public void setEvent() {
		setEvent(true);
	}

	public void setEvent(boolean flag) {
		List<AIRuntime<RESP_TYPE>> tmp;
		synchronized (this) {
			m_BeSign = flag;
			if (!flag) {
				return;
			}
			tmp = this.waiting;
			this.waiting = new LinkedList<AIRuntime<RESP_TYPE>>();
		}
		for (AIRuntime<RESP_TYPE> rt : tmp) {
			try {
				rt.run(null);
			} catch (Exception e) {
			}
		}
		Thread.yield();
	}

	public void resetEvent() {
		setEvent(false);
	}

	public boolean waitEvent(AIFunction<RESP_TYPE> fun,
			final AIStack<RESP_TYPE> stack, long timeout) {
		synchronized (this) {
			if (!_checkEvent()) {
				final Exception tme = new AITimeoutException(
						"AIEvent.waitEvent");
				AITimeoutTask<RESP_TYPE> task = new AITimeoutTask<RESP_TYPE>(
						stack, AIContextThread.create(), timeout) {
					@Override
					protected void stackTimeout(AIContext ctx,
							AIStack<RESP_TYPE> stack) {
						AIEvent.this.cancel(stack);
						AIUtil.executeFailure(ctx, stack, tme);
					}
				};
				TimerManager tmobj = timer;
				if (tmobj == null)
					tmobj = AIVM.current().sureTimer();
				waiting.add(AIRuntime.create(fun, task));
				tmobj.postTimerTask(task);
				return false;
			}
		}
		return fun.apply(stack);
	}

	public boolean waitEvent(AIFunction<RESP_TYPE> fun, AIStack<RESP_TYPE> stack) {
		synchronized (this) {
			if (!_checkEvent()) {
				waiting.add(AIRuntime.create(fun, stack));
				return false;
			}
		}
		return fun.apply(stack);
	}

	protected boolean _checkEvent() {
		boolean r = m_BeSign;
		if (r && m_AutoReset)
			m_BeSign = false;
		return r;
	}

	public boolean checkEvent() {
		synchronized (this) {
			return _checkEvent();
		}
	}

	public void clear() {
		synchronized (this) {
			this.waiting.clear();
		}
	}

	public void allFailure(Throwable t) {
		List<AIRuntime<RESP_TYPE>> tmp = null;
		synchronized (this) {
			tmp = new ArrayList<AIRuntime<RESP_TYPE>>(waiting);
			this.waiting.clear();
		}

		for (AIRuntime<RESP_TYPE> r : tmp) {
			AIUtil.safeFailure(r.getStack(), t);
		}
	}

	public void cancel(AIStack<RESP_TYPE> stack) {
		synchronized (this) {
			Iterator<AIRuntime<RESP_TYPE>> it = this.waiting.iterator();
			while (it.hasNext()) {
				AIRuntime<RESP_TYPE> rt = it.next();
				if (ObjectUtil.equals(rt.getStack(), stack)) {
					it.remove();
				}
			}
		}
	}

}
